﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Binalyse.Memory
{
    public enum SearchType
    {
        Byte, Short, Int, Float, Double, ByteArray
    }
    public class BinarySearch
    {
        private int nCurrentIndex = -1;
        private byte[] buffer = null;
        private bool bIsPreciseSearch = true;
        private object objSearchValue = null;
        private object objUpLimit = null;
        private object objDownLimit = null;
        private int nSpacing = 4;
        private SearchType stSearchType = SearchType.Int;

        #region 构造函数

        public BinarySearch() { }
        //精密搜索
        public BinarySearch(object toSearch, int spacing, SearchType type)
        {
            bIsPreciseSearch = true;
            objSearchValue = toSearch;
            objUpLimit = null;
            objDownLimit = null;
            nSpacing = spacing;
            stSearchType = type;
        }
        //范围搜索
        public BinarySearch(object upLimit, object downLimit, int spacing, SearchType type)
        {
            bIsPreciseSearch = false;
            objSearchValue = null;
            objUpLimit = upLimit;
            objDownLimit = downLimit;
            nSpacing = spacing;
            stSearchType = type;
        }

        #endregion

        #region Properties

        public bool IsPreceiseSearch 
        {
            get { return bIsPreciseSearch; }
            set { bIsPreciseSearch = value; }
        }

        public object SearchValue
        {
            get { return objSearchValue; }
            set { objSearchValue = value; }
        }

        public object UpLimit
        {
            get { return objUpLimit; }
            set { objUpLimit = value; }
        }

        public object DownLimit
        {
            get { return objDownLimit; }
            set { objDownLimit = value; }
        }

        public int Spacing
        {
            get { return nSpacing; }
            set { if (value > 0) nSpacing = value; }
        }

        public SearchType SearchType
        {
            get { return stSearchType; }
            set { stSearchType = value; }
        }

        #endregion

        private void SearchByte(byte to)
        {
            int nStart = nCurrentIndex == -1 ? 0 : nCurrentIndex + nSpacing;
            int length = buffer.Length;
            bool bNotFounded = true;
            for (; nStart < length && bNotFounded; nStart += nSpacing)
            {
                if (buffer[nStart] == to)
                {
                    bNotFounded = true;
                    nCurrentIndex = nStart;
                }
            }
            if (bNotFounded) nCurrentIndex = -1;
        }
        private void SearchByte(byte up, byte down)
        {
            int nStart = nCurrentIndex == -1 ? 0 : nCurrentIndex + nSpacing;
            int length = buffer.Length - 3;
            bool bNotFounded = true;
            for (; nStart < length && bNotFounded; nStart += nSpacing)
            {
                if (buffer[nStart] <= up && buffer[nStart] >= down)
                {
                    bNotFounded = true;
                    nCurrentIndex = nStart;
                }
            }
            if (bNotFounded) nCurrentIndex = -1;
        }
        private void SearchShort(short to)
        {
            int nStart = nCurrentIndex == -1 ? 0 : nCurrentIndex + nSpacing;
            int length = buffer.Length - 1;
            bool bNotFounded = true;
            byte[] tos = BitConverter.GetBytes(to);
            for (; nStart < length && bNotFounded; nStart += nSpacing)
            {
                if (buffer[nStart] == tos[0] && buffer[nStart + 1] == tos[1])
                {
                    bNotFounded = true;
                    nCurrentIndex = nStart;
                }
            }
            if (bNotFounded) nCurrentIndex = -1;
        }
        private void SearchShort(short up, short down)
        {
            int nStart = nCurrentIndex == -1 ? 0 : nCurrentIndex + nSpacing;
            int length = buffer.Length - 3;
            bool bNotFounded = true;
            short tmpVal = (short)0;
            for (; nStart < length && bNotFounded; nStart += nSpacing)
            {
                tmpVal = BitConverter.ToInt16(buffer, nStart);
                if (tmpVal <= up && tmpVal >= down)
                {
                    bNotFounded = true;
                    nCurrentIndex = nStart;
                }
            }
            if (bNotFounded) nCurrentIndex = -1;
        }
        private void SearchInt(int to)
        {
            int nStart = nCurrentIndex == -1 ? 0 : nCurrentIndex + nSpacing;
            int length = buffer.Length - 3;
            bool bNotFounded = true;
            byte[] tos = BitConverter.GetBytes(to);
            for (; nStart < length && bNotFounded; nStart += nSpacing)
            {
                if (buffer[nStart] == tos[0] && buffer[nStart + 1] == tos[1]
                    && buffer[nStart + 2] == tos[2] && buffer[nStart + 3] == tos[3])
                {
                    bNotFounded = true;
                    nCurrentIndex = nStart;
                }
            }
            if (bNotFounded) nCurrentIndex = -1;
        }
        private void SearchInt(int up,int down)
        {
            int nStart = nCurrentIndex == -1 ? 0 : nCurrentIndex + nSpacing;
            int length = buffer.Length - 3;
            bool bNotFounded = true;
            int tmpVal = (int)0;
            for (; nStart < length && bNotFounded; nStart += nSpacing)
            {
                tmpVal = BitConverter.ToInt32(buffer, nStart);
                if (tmpVal <= up && tmpVal >= down)
                {
                    bNotFounded = true;
                    nCurrentIndex = nStart;
                }
            }
            if (bNotFounded) nCurrentIndex = -1;
        }
        private void SearchByteArray(byte[] to)
        {
            int nStart = nCurrentIndex == -1 ? 0 : nCurrentIndex + nSpacing;
            int length = buffer.Length - to.Length - 1;
            bool bNotFounded = true;
            for (; nStart < length && bNotFounded; nStart += nSpacing)
            {
                bool bEqual = true;
                for (int nStart1 = 0; nStart1 < to.Length && bEqual; nStart1++)
                {
                    bEqual = (to[nStart1] == buffer[nStart + nStart1]);
                }
                if (bEqual)
                {
                    bNotFounded = true;
                    nCurrentIndex = nStart;
                }
            }
            if (bNotFounded) nCurrentIndex = -1;
        }
        private void SearchSingle(float up, float down)
        {
            int nStart = nCurrentIndex == -1 ? 0 : nCurrentIndex + nSpacing;
            int length = buffer.Length - 3;
            bool bNotFounded = true;
            float tmpVal = 0.0f;
            for (; nStart < length && bNotFounded; nStart += nSpacing)
            {
                tmpVal = BitConverter.ToSingle(buffer, nStart);
                if (tmpVal <= up && tmpVal >= down)
                {
                    bNotFounded = true;
                    nCurrentIndex = nStart;
                }
            }
            if (bNotFounded) nCurrentIndex = -1;
        }
        private void SearchDouble(double up, double down)
        {
            int nStart = nCurrentIndex == -1 ? 0 : nCurrentIndex + nSpacing;
            int length = buffer.Length - 7;
            bool bNotFounded = true;
            double tmpVal = 0.0;
            for (; nStart < length && bNotFounded; nStart += nSpacing)
            {
                tmpVal = BitConverter.ToDouble(buffer, nStart);
                if (tmpVal <= up && tmpVal >= down)
                {
                    bNotFounded = true;
                    nCurrentIndex = nStart;
                }
            }
            if (bNotFounded) nCurrentIndex = -1;
        }

        public int StartSearch(byte[] data)
        {
            buffer = data;
            nCurrentIndex = -1;
            return Next();
        }

        public int Next()
        {
            if (bIsPreciseSearch)
            {
                switch (stSearchType)
                {
                    case Memory.SearchType.Byte:
                        if (objSearchValue is byte) SearchByte((byte)objSearchValue);
                        break;
                    case Memory.SearchType.Short:
                        if (objSearchValue is short) SearchShort((short)objSearchValue);
                        break;
                    case Memory.SearchType.Int:
                        if (objSearchValue is int) SearchInt((int)objSearchValue);
                        break;
                    case Memory.SearchType.ByteArray:
                        if (objSearchValue is byte[]) SearchByteArray((byte[])objSearchValue);
                        break;
                }
            }
            else
            {
                switch (stSearchType)
                {
                    case Memory.SearchType.Byte:
                        if (objUpLimit is byte && objDownLimit is byte) SearchByte((byte)objUpLimit, (byte)objDownLimit);
                        break;
                    case Memory.SearchType.Short:
                        if (objUpLimit is short && objDownLimit is short) SearchShort((short)objUpLimit, (short)objDownLimit);
                        break;
                    case Memory.SearchType.Int:
                        if (objUpLimit is int && objDownLimit is int) SearchInt((int)objUpLimit, (int)objDownLimit);
                        break;
                    case Memory.SearchType.Float:
                        if (objUpLimit is float && objDownLimit is float) SearchSingle((float)objUpLimit, (float)objDownLimit);
                        break;
                    case Memory.SearchType.Double:
                        if (objUpLimit is double && objDownLimit is double) SearchDouble((double)objUpLimit, (double)objDownLimit);
                        break;
                }
            }
            return nCurrentIndex;
        }
    }
}
